package com.xci.platform.auth.service;

import cn.hutool.core.date.DateTime;
import com.github.pagehelper.Page;
import com.xci.platform.annotation.OperateUser;
import com.xci.platform.annotation.QueryMap;
import com.xci.platform.auth.core.AuthConstant;
import com.xci.platform.auth.core.HistoryModel;
import com.xci.platform.auth.core.HistoryOperateType;
import com.xci.platform.auth.dao.SequenceDao;
import com.xci.platform.auth.entity.SequenceEntity;
import com.xci.platform.core.BoolMessage;
import com.xci.platform.core.Constant;
import com.xci.platform.helper.CoreHelper;
import com.xci.platform.helper.StringHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 系统序列服务
 */
@Service
public class SequenceService extends AuthBaseService {

    /**
     * 系统序列数据访问对象
     */
    @Resource
    private SequenceDao sequenceDao;

    /**
     * 保存系统序列
     *
     * @param entity   系统序列对象
     * @param isCreate 是否新建
     */
    @Validated
    @OperateUser
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage save(SequenceEntity entity, Boolean isCreate) {
        //系统序列名称验证
        if (sequenceDao.existByCode(entity.getCode(), entity.getId())) {
            String msg = StringHelper.format("系统序列编码 {} 已经存在", entity.getCode());
            return new BoolMessage(false, msg);
        }

        String msg;
        long startTime = CoreHelper.startWatch();
        if (isCreate) {
            msg = StringHelper.format("新增系统序列 {}", entity.getCode());
            sequenceDao.insert(entity);
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Insert)
                    .keyValue(entity.getId())
                    .message(msg)
                    .after(entity)
            );
            //endregion
        } else {
            msg = StringHelper.format("修改系统序列 {}", entity.getCode());
            SequenceEntity oldEntity = queryById(entity.getId());
            sequenceDao.update(entity);
            //region 历史记录
            operateHistory(buildHistory()
                    .operateType(HistoryOperateType.Update)
                    .keyValue(entity.getId())
                    .message(msg)
                    .before(oldEntity)
                    .after(entity)
            );
            //endregion
        }
        // 操作日志
        operateLog(AuthConstant.SystemModule.Sequence, msg, CoreHelper.stopWatch(startTime));
        return BoolMessage.True;
    }

    /**
     * 删除系统序列
     *
     * @param id 系统序列主键数组
     */
    @Validated
    @Transactional(rollbackFor = Exception.class)
    public BoolMessage delete(@NotEmpty(message = "请指定删除的系统序列主键") String id) {
        SequenceEntity oldEntity = sequenceDao.queryById(id);
        if (oldEntity == null) return BoolMessage.True;

        long startTime = CoreHelper.startWatch();
        sequenceDao.delete(id);
        String msg = StringHelper.format("删除系统序列 {}", oldEntity.getCode());
        //region 历史记录
        operateHistory(buildHistory()
                .operateType(HistoryOperateType.Delete)
                .keyValue(id)
                .message(msg)
                .before(oldEntity)
        );
        //endregion
        // 操作日志
        operateLog(AuthConstant.SystemModule.Sequence, msg, CoreHelper.stopWatch(startTime));
        return BoolMessage.True;
    }

    /**
     * 根据系统序列主键获取系统序列对象
     *
     * @param id 系统序列主键
     * @return 返回指定系统序列主键的系统序列对象
     */
    @Validated
    public SequenceEntity queryById(@NotNull(message = "请指定系统序列主键") String id) {
        return sequenceDao.queryById(id);
    }

    /**
     * 根据系统序列编码获取系统序列值
     *
     * @param code 系统序列编码
     * @return 返回系统序列编码对应的系统序列值
     */
    @Validated
    public SequenceEntity queryByCode(@NotNull(message = "请指定系统序列编码") String code) {
        return sequenceDao.queryByCode(code);
    }

    /**
     * 获取下一个序列值
     *
     * @param code 系统序列编码
     * @param name 系统序列名称
     */
    @Transactional(rollbackFor = Exception.class)
    @OperateUser
    @Validated
    public Long nextValue(@NotNull(message = "请指定系统序列编码") String code, String name) {
        return nextValueCore(code, name);
    }

    /**
     * 获取下一个序列值
     *
     * @param code 系统序列编码
     */
    @Transactional(rollbackFor = Exception.class)
    @Validated
    public Long nextValue(@NotNull(message = "请指定系统序列编码") String code) {
        return nextValue(code, code);
    }

    /**
     * 获取当前序列值
     *
     * @param code 系统序列编码
     */
    @Validated
    public Long currentValue(@NotNull(message = "请指定系统序列编码") String code) {
        return sequenceDao.currentValue(code);
    }

    /**
     * 获取当前格式化的序列值
     *
     * @param code 系统序列编码
     */
    @Transactional(rollbackFor = Exception.class)
    @Validated
    public String currentFormatValue(@NotBlank(message = "请指定系统序列编码") String code) {
        return formatTpl(code);
    }

    /**
     * 获取下一个格式化的序列值
     *
     * @param code 系统序列编码
     */
    @Transactional(rollbackFor = Exception.class)
    @Validated
    public String nextFormatValue(@NotBlank(message = "请指定系统序列编码") String code) {
        String result = formatTpl(code);
        sequenceDao.incrementValue(code);
        return result;
    }

    /**
     * 验证指定系统序列编码是否存在
     *
     * @param code 系统序列编码
     * @return 如果存在返回true, 否则返回false
     */
    Boolean existByCode(String code) {
        return sequenceDao.existByCode(code, null);
    }

    /**
     * 查询系统序列列表
     *
     * @param params 查询系统序列
     * @return 返回符合查询条件的系统序列列表
     */
    @QueryMap
    public List<SequenceEntity> query(Map params) {
        return sequenceDao.query(params);
    }

    /**
     * 查询系统序列分页列表
     *
     * @param params 查询系统序列
     * @return 返回符合查询条件的系统序列分页列表
     */
    @QueryMap
    public Page<SequenceEntity> queryPage(Map params) {
        return (Page<SequenceEntity>) sequenceDao.query(params);
    }

    /**
     * 生成历史记录对象
     */
    private HistoryModel.HistoryModelBuilder buildHistory() {
        return HistoryModel.builder()
                .tableName("sys_sequence")
                .tableCaption("系统序列")
                .keyName("id");
    }

    /**
     * 获取当前序列值并更新
     *
     * @param code 序列编码
     * @param name 序列名称
     */
    private Long nextValueCore(String code, String name) {
        if (StringHelper.isBlank(name)) {
            name = code;
        }
        Long nextValue = sequenceDao.currentValue(code);
        if (nextValue == null) {
            //新增
            nextValue = 1L;
            SequenceEntity entity = new SequenceEntity();
            entity.setId(StringHelper.guid());
            entity.setCode(code);
            entity.setName(name);
            entity.setSeqStart(1);
            entity.setSeqValue(nextValue);
            entity.setSeqStep(1);
            entity.setTpl(Constant.Empty);
            entity.setOperateUserId("system");
            entity.setOperateUserName("system");
            entity.setOperateDateTime(new Date());
            this.save(entity, true);
        }
        sequenceDao.incrementValue(code);
        return nextValue;
    }

    /**
     * 格式化模板
     * @param code 系统序列编码
     */
    private String formatTpl(String code){
        SequenceEntity entity = sequenceDao.queryByCode(code);
        Assert.notNull(entity, "指定的系统序列编码(" + code + ")不存在");

        String tpl = entity.getTpl();
        Assert.hasText(tpl,"指定的系统序列(" + code + ")没有设置模板");

        Long currentValue = entity.getSeqValue();
        DateTime dateTime = DateTime.now();
        HashMap<String, String> paramMap = new HashMap<>();
        paramMap.put("{seq}", String.valueOf(currentValue));
        paramMap.put("{yyyy}", dateTime.toString("yyyy"));
        paramMap.put("{MM}", dateTime.toString("MM"));
        paramMap.put("{dd}", dateTime.toString("dd"));
        paramMap.put("{HH}", dateTime.toString("HH"));
        paramMap.put("{mm}", dateTime.toString("mm"));
        paramMap.put("{ss}", dateTime.toString("ss"));
        for (Map.Entry<String, String> item : paramMap.entrySet()) {
            tpl = tpl.replace(item.getKey(), item.getValue());
        }
        return tpl;
    }
}
